import { Store } from "./store";
import {
	getSafeName,
	deepClone,
	checkOptions,
	getSafeValue,
	isArrayStore,
	getOptionStore,
	getOptionName
} from "./utils";

export interface ProvideOptions {
	name?: any;
	store?: Store | Store[];
	args?: any[];
}

export interface OfferOptions extends ProvideOptions {
	pure?: boolean;
}

/**
 * offer a value to be injected manually
 * @param target inject a value
 * @param options set options to control the status in store
 */
export function Offer(target: any, options?: OfferOptions) {
	if (checkOptions(options)) {
		throw new Error("invaild store is injected");
	}

	const safeValue = getSafeValue(target, options);
	const safeName = getOptionName(target, options);
	const safeStore = getOptionStore(options);

	if (isArrayStore(safeStore)) {
		for (let i in safeStore) {
			if (safeStore[i].exist(safeName)) {
				throw new Error("it can only provide a unique name");
			}
			safeStore[i].add({
				name: safeName,
				value: deepClone(safeValue)
			});
		}
	} else {
		if (safeStore.exist(safeName)) {
			throw new Error("it can only provide a unique name");
		}
		safeStore.add({
			name: safeName,
			value: deepClone(safeValue)
		});
	}
}

/**
 * Provide a class to be injected
 * @param options set options to control the status in store
 */
export function Provide(options?: ProvideOptions) {
	return (target: any) => {
		Offer(target, options);
	};
}

/**
 * generate a token for being injected
 * @param target the value want to be injected
 */
export function createToken() {
	const randomNumber1 = Math.random();
	const randomNumber2 = Math.random() * Math.random();
	const randomNumber3 = Math.random() * new Date().getTime();
	return getSafeName(
		`${randomNumber1.toString()}/${randomNumber2.toString()}/${randomNumber3.toString()}`
	);
}
